package com.yfq.springframework.beans.support;

import com.yfq.springframework.beans.BeansException;
import com.yfq.springframework.beans.DisposableBean;
import com.yfq.springframework.beans.ObjectFactory;
import com.yfq.springframework.beans.config.SingletonBeanRegistry;
import com.yfq.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 姚凤桥
 * @date 2023/1/8 17:09
 * @description Spring的三级缓存：
 * 1.singletonObjects：一级缓存，存放经历过完整生命周期的bean
 * 2.earlySingletonObjects：二级缓存，存放没有经历过完整生命周期的bean
 * 3.singletonFactories：三级缓存，存放工厂对象
 * Spring使用三级缓存解决循环依赖的步骤:
 * 1.在单例bean被实例化后，将其包装成为一个ObjectFactory存放到singletonFactories中
 * 2.在进行依赖注入调用到getBean放法时，会先从三级缓存中获取依次获取，如果获取不到才会创建
 * 3.当产生循环依赖时，因为bean被实例化后就放到了三级缓存中，所以必定可以从三级缓存中获取到需要的bean对象(可能是没有经历过完整生命周期的bean)
 * 4.当从三级缓存中获取到bean后进行依赖注入，当bean经历过完整的生命周期后将其存放到一级缓存中，如果二级、三级缓存中有这个bean需要删除、
 * 上面的步骤只是粗略分析，具体分析需要从循环依赖不同来分类
 * 产生循环依赖的三种情况：
 * 1.A依赖自己
 * 2.A依赖B，B依赖A
 * 3.A依赖B，B依赖C，C依赖A
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    /**
     * 一级缓存，缓存普通对象(经历过完整生命周期)
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * 二级缓存，提前暴露对象，没有经历过完整生命周期的bean
     */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    /**
     * 三级缓存，存放单例工厂对象
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
    /**
     * 缓存bean的销毁方法
     */
    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    @Override
    public void registerSingletonBean(String beanName, Object bean) {
        //注册单例对象的时候注册到一级缓存中，同时如果二级、三级缓存中有这个对象，需要删除
        singletonObjects.put(beanName, bean);

        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    @Override
    public Object getSingletonBean(String beanName) {
        //从一级缓存中获取
        Object bean = singletonObjects.get(beanName);

        if (bean != null) {
            return bean;
        }

        //从二级缓存中获取
        bean = earlySingletonObjects.get(beanName);
        if (bean != null) {
            return bean;
        }

        //从三级缓存中获取工厂对象
        ObjectFactory<?> objectFactory = singletonFactories.get(beanName);

        if (objectFactory != null) {
            //从三级缓存的工厂对象上获取到真实的对象，添加到二级缓存中
            earlySingletonObjects.put(beanName, objectFactory.getObject());

            //从三级缓存中删除工厂对象
            singletonFactories.remove(beanName);

            return objectFactory.getObject();
        }

        return null;
    }

    /**
     * Add the given singleton factory for building the specified singleton
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            //如果一级缓存里面不包含这个对象，才会将这个工厂对象添加到三级缓存中
            if (!this.singletonObjects.containsKey(beanName)) {
                this.earlySingletonObjects.remove(beanName);

                this.singletonFactories.put(beanName, singletonFactory);
            }
        }
    }


    public void registerDisposableBean(String beanName, DisposableBeanAdapter disposableBeanAdapter) {
        disposableBeans.put(beanName, disposableBeanAdapter);
    }

    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        String[] disposableBeanNames = keySet.toArray(new String[0]);

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception");
            }
        }
    }

}
